package comp302.prototype.component;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Stroke;
import java.util.ArrayList;
import java.util.Iterator;

import comp302.prototype.misc.MiscMath;
import comp302.prototype.settings.LineSettings;
import comp302.prototype.xml.XMLNode;

public class Zigzag implements Component {
	private ArrayList<Point> points = new ArrayList<Point>();
	private Point lowPoint = new Point(), highPoint = new Point();
	private Color color;
	private int thickness;
	private int selected = -2;
	private final int ALL = -1, NOTHING = -2;
	
	public Zigzag() {
		points.add(new Point());
		points.add(new Point());
		color = Color.RED;
		thickness = 3;
		refreshHighLow();
	}

	public Zigzag(int x1, int y1, int x2, int y2, Color color, int thickness) {
		points.add(new Point(x1, y1));
		points.add(new Point(x2, y2));
		refreshHighLow();
		this.color = color;
		this.thickness = thickness;
	}

	@Override
	public String getName() {
		return "Zigzag";
	}

	public void setEndPoint(int x, int y) {
		(points.get(points.size() - 1)).move(x, y);
		refreshHighLow();
	}

	public void addPoint(int x, int y) {
		points.add(new Point(x, y));
		updateHighLow(x, y);
	}

	public void removePointAt(int index) {
		points.remove(index);
		refreshHighLow();
	}

	public void removeLast() {
		removePointAt(points.size() - 1);
		refreshHighLow();
	}

	private void updateHighLow(int x, int y) {

		if (x < lowPoint.getX()) {
			lowPoint.setLocation(x, (int) lowPoint.getY());
		} else if (x > highPoint.getX()) {
			highPoint.setLocation(x, (int) highPoint.getY());
		}

		if (y < lowPoint.getY()) {
			lowPoint.setLocation((int) lowPoint.getX(), y);
		} else if (y > highPoint.getY()) {
			highPoint.setLocation((int) highPoint.getX(), y);
		}
	}

	private void refreshHighLow() {
		lowPoint.setLocation(points.get(0));
		highPoint.setLocation(points.get(0));

		for (int i = 1; i < points.size(); i++) {
			updateHighLow((int) points.get(i).getX(), (int) points.get(i)
					.getY());
		}
	}

	@Override
	public void draw(Graphics g) {

		for (int i = 0; i < points.size() - 1; i++) {
			(new Line(points.get(i), points.get(i + 1), color, thickness))
					.draw(g);
		}

		drawSelection(g);
	}

	private void drawSelection(Graphics g) {
		int tx, ty, x1, y1, x2, y2;
		Graphics2D g2d = (Graphics2D) g;
		Color previousColor = g.getColor();
		Stroke previousStroke = g2d.getStroke();

		if (selected == NOTHING) {
			return;
		} else if (selected == ALL) {
			x1 = (int) lowPoint.getX();
			y1 = (int) lowPoint.getY();
			x2 = (int) highPoint.getX();
			y2 = (int) highPoint.getY();
		} else {
			tx = (int) points.get(selected).getX();
			ty = (int) points.get(selected).getY();
			x1 = tx - 4;
			y1 = ty - 4;
			x2 = tx + 4;
			y2 = ty + 4;
		}

		x1 -= thickness;
		y1 -= thickness;
		x2 += thickness;
		y2 += thickness;

		g2d.setStroke(new BasicStroke(2));
		g.setColor(Color.BLACK);
		g2d.drawLine(x1, y1, x1 + thickness, y1);
		g2d.drawLine(x1, y1, x1, y1 + thickness);
		g2d.drawLine(x2, y2, x2 - thickness, y2);
		g2d.drawLine(x2, y2, x2, y2 - thickness);
		g2d.setStroke(previousStroke);
		g.setColor(previousColor);
	}

	@Override
	public boolean isPointInside(Point p) {

		if (lowPoint.getX() <= p.getX() && p.getX() <= highPoint.getX()
				&& lowPoint.getY() <= p.getY() && p.getY() <= highPoint.getY()) {
			return true;
		} else {
			boolean insidePoint = false;

			for (int i = 0; i < points.size(); i++) {
				insidePoint |= points.get(i).distance(p) <= thickness + 3;
			}

			return insidePoint;
		}
	}

	@Override
	public boolean select(int x, int y) {
		Point selectionPoint = new Point(x, y);

		for (int i = 0; i < points.size(); i++) {

			if (selectionPoint.distance(points.get(i)) <= thickness + 3) {
				selected = i;
				return true;
			}
		}

		for (int i = 0; i < points.size() - 1; i++) {

			if (MiscMath.distanceToLine(points.get(i), points.get(i + 1),
					selectionPoint) <= thickness + 3) {
				selected = ALL;
				return true;
			}
		}

		selected = NOTHING;
		return false;
	}

	@Override
	public void deselect() {
		selected = NOTHING;
	}

	@Override
	public boolean isSelected() {
		return (selected == NOTHING) ? false : true;
	}

	@Override
	public void move(int x, int y) {

		if (selected == NOTHING) {
			return;
		}

		if (selected == ALL) {

			for (Point point : points) {
				point.translate(x, y);
			}

			lowPoint.translate(x, y);
			highPoint.translate(x, y);
		} else {
			points.get(selected).translate(x, y);
			refreshHighLow();
		}
	}

	@Override
	public void updateAttributes() {
		thickness = LineSettings.getThickness();
		color = LineSettings.getColor();
	}

	@Override
	public boolean boundaryCheck(int width, int height) {
		double xl = lowPoint.getX();
		double yl = lowPoint.getY();
		double xh = highPoint.getX();
		double yh = highPoint.getY();

		return (xl < 0 || yl < 0 || xh > width || yh > height) ? false : true;
	}

	@Override
	public XMLNode toXML() {
		XMLNode node = new XMLNode("zigzag");
		Iterator<Point> iterator = points.iterator();

		node.setAttribute("thickness", Integer.toString(thickness));
		node.setAttribute("color", Integer.toString(color.getRGB()));

		while (iterator.hasNext()) {
			XMLNode pointNode = new XMLNode("point");
			Point point = iterator.next();
			
			pointNode.setAttribute("x", Integer.toString((int) point.getX()));
			pointNode.setAttribute("y", Integer.toString((int) point.getY()));
			node.addChild(pointNode);
		}

		return node;
	}

	@Override
	public String toString() {
		return toXML().toString();
	}

	@Override
	public void fromXML(XMLNode node) {
		Iterator<XMLNode> iterator = node.childIterator();
		thickness = Integer.parseInt(node.getAttribute("thickness"));
		color = new Color(Integer.parseInt(node.getAttribute("color")));
		points = new ArrayList<Point>();
		
		while (iterator.hasNext()) {
			XMLNode pointNode = iterator.next();
			int x = Integer.parseInt(pointNode.getAttribute("x"));
			int y = Integer.parseInt(pointNode.getAttribute("y"));
			points.add(new Point(x, y));
		}
		
		refreshHighLow();
	}
}
